package com.siro.auth.utils.excel;

import cn.hutool.core.util.ObjectUtil;
import com.siro.auth.base.constant.Constants;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * 导出excel文件
 *
 * @author starsea
 * @date 2022-11-12 15:06
 */
public class ExportExcel {

    private static final Logger log = LoggerFactory.getLogger(ExportExcel.class);

    private ExportDataEntity exportData;
    private List<String> headerList = new ArrayList<>();
    private String isAll;
    private String fileType;
    private int sheetNo = 0;
    private File file;
    private SXSSFWorkbook wb;
    private Sheet sheet;
    private Map<String, CellStyle> styles;
    public int rowNum;
    private String title;

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public ExportExcel(String title) {
        this.wb = new SXSSFWorkbook(500);
        this.title = title;
    }

    public ExportExcel(String title, String[] headers) {
        initialize(title, Arrays.asList(headers));
    }

    public ExportExcel(String title, List<String> headerList) {
        initialize(title, headerList);
    }

    public void initializeTitle(String isAll, String fileType) {
        this.isAll = isAll;
        this.fileType = fileType;
        if (this.headerList.size() == 0) {
            List<ExcelColumnProperty> nameMap = this.exportData.getNameMap();
            for (ExcelColumnProperty entity : nameMap) {
                this.headerList.add(entity.getNamCn());
            }
        }
        if ("1".equals(fileType)) {
            initialize(this.title, this.headerList);
        } else {
            initializeCsvHeader(this.title, this.headerList);
        }
    }

    private void initializeCsvHeader(String title, List<String> headerList) {
        String[] header = new String[headerList.size()];
        for (int i = 0; i < headerList.size(); i++) {
            header[i] = (String) headerList.get(i);
        }
    }

    private void initialize(String title, List<String> asList) {
        this.sheet = this.wb.createSheet(title + this.sheetNo);
        this.sheetNo += 1;
        this.styles = createStyles(this.wb);

        if (headerList == null || headerList.size() == 0) {
            return;
        }

        if (StringUtils.isNotBlank(title)) {
            Row titleRow = this.sheet.createRow(this.rowNum++);
            titleRow.setHeightInPoints(30.0F);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle((CellStyle) this.styles.get("title"));
            titleCell.setCellValue(title);
            CellRangeAddress cellAddresses = new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), headerList.size() - 1);
            if (titleRow.getRowNum() < headerList.size() - 1) {
                this.sheet.addMergedRegion(cellAddresses);
            }
        }
        Row headerRow = this.sheet.createRow(this.rowNum++);
        headerRow.setHeightInPoints(16.0F);
        Map<Integer, Integer> sizeMap = new HashMap<>();
        sizeMap.put(0, 0);
        for (int i = 0; i < headerList.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellStyle((CellStyle) this.styles.get("header"));
            String[] ss = StringUtils.split(headerList.get(i), "**", 2);
            if (ss.length == 2) {
                cell.setCellValue(ss[0]);
                Comment comment = this.sheet.createDrawingPatriarch().createCellComment(
                        new XSSFClientAnchor(0, 0, 0, 0, 3, 3, 5, 6));
                comment.setString(new XSSFRichTextString(ss[1]));
                cell.setCellComment(comment);
            } else {
                cell.setCellValue(headerList.get(i));
            }
            for (Integer index : sizeMap.keySet()) {
                sheet.setColumnWidth(index, sizeMap.get(index) * 256);
            }
        }
        for (int i = 0; i < headerList.size(); i++) {
            int colWidth = this.sheet.getColumnWidth(i) * 2;
            this.sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth);
        }
        log.debug("Initialize success.");
    }

    private Map<String, CellStyle> createStyles(SXSSFWorkbook wb) {
        Map styles = new HashMap();

        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setBold(true);
        style.setFont(titleFont);
        styles.put("title", style);
        style = wb.createCellStyle();
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());

        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeight((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom((CellStyle) styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        styles.put("data1", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom((CellStyle) styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        styles.put("data2", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom((CellStyle) styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        styles.put("data3", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom((CellStyle) styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.getIndex());
        Font headerFont = wb.createFont();
        headerFont.setFontName("Arial");
        headerFont.setFontHeight((short) 10);
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.BLACK.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);

        return styles;
    }

    public Row addRow() {
        return this.sheet.createRow(this.rowNum++);
    }

    public Cell addCell(Row row, int column, Object val) {
        return addCell(row, column, val, 2, Class.class);
    }

    public Cell addCell(Row row, int column, Object val, int styleFlag) {
        return addCell(row, column, val, styleFlag, Class.class);
    }

    public Cell addCell(Row row, int column, Object val, int align, Class<?> fieldType) {
        Cell cell = row.createCell(column);
        CellStyle style = this.styles.get("data" + ((align >= 1) && (align <= 3) ? Integer.valueOf(align) : ""));
        try {
            if (ObjectUtil.isNull(val)) {
                cell.setCellValue("");
            } else if ((val instanceof String)) {
                cell.setCellValue((String) val);
            } else if ((val instanceof Integer)) {
                cell.setCellValue(val + "");
            } else if ((val instanceof Long)) {
                cell.setCellValue(((Long) val).longValue());
            } else if (val instanceof Double) {
                cell.setCellValue(((Double) val).doubleValue());
            } else if (val instanceof Float) {
                cell.setCellValue(((Float) val).floatValue());
            } else if (val instanceof BigDecimal) {
                cell.setCellValue(val.toString());
            } else if (val instanceof Date) {
                DataFormat format = this.wb.createDataFormat();
                style.setDataFormat(format.getFormat("yyyy-MM-dd"));
                cell.setCellValue((Date) val);
            } else if (fieldType != Class.class) {
                cell.setCellValue((String) fieldType.getMethod("setValue", new Class[]{Object.class}).invoke(null, new Object[]{val}));
            } else {
                cell.setCellValue((String) Class.forName(getClass().getName().replaceAll(getClass().getSimpleName(),
                        "fieldtype." + val.getClass().getSimpleName() + "Type")).getMethod("setValue", new Class[]{Object.class}).invoke(null, new Object[]{val}));
            }
        } catch (Exception e) {
            log.error("Set cell value [" + row.getRowNum() + "," + column + "] error: " + e.toString());
            cell.setCellValue(val.toString());
        }
        cell.setCellStyle(style);
        return cell;
    }

    public ExportExcel setDataList(List<Map<String, String>> list) {
        for (Map<String, String> map : list) {
            int column = 0;
            Row row = addRow();
            Object val;
            for (ExcelColumnProperty entity : exportData.getNameMap()) {
                val = map.get(entity.getNamEn());
                if (StringUtils.isNotBlank(entity.getDictType())) {
                    Map<String, String> dictMap = entity.getDictMap();
                    if (Constants.DICT_TYPE_USER.equals(entity.getDictType())) {
                        val = dictMap.get(val) == null ? val : val + "/" + dictMap.get(val);
                    } else {
                        val = dictMap.get(val) == null ? val : dictMap.get(val);
                    }
                }
                int styleFlag = 0;
                if (!StringUtils.isEmpty(entity.getFlgStyle())) {
                    styleFlag = Integer.parseInt(entity.getFlgStyle());
                }
                addCell(row, column++, val, styleFlag);
            }
        }
        return this;
    }

    public ExportExcel write(OutputStream os) throws IOException {
        this.wb.write(os);
        return this;
    }

    public ExportExcel write(HttpServletResponse response, String fileName) throws IOException {
        response.reset();
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        write(response.getOutputStream());
        return this;
    }

    public ExportExcel writeFile(String name) throws IOException {
        FileOutputStream os = new FileOutputStream(name);
        write(os);
        return this;
    }

    public ExportExcel dispose() {
        this.wb.dispose();
        return this;
    }

    public ExportDataEntity getExportData() {
        return this.exportData;
    }

    public void setExportData(ExportDataEntity exportData) {
        if ((exportData != null) && exportData.getNameMap() != null) {
            this.exportData = exportData;
        } else {
            List nameMap = new ArrayList();
            this.exportData = new ExportDataEntity();
            this.exportData.setNameMap(nameMap);
        }
    }

    public void downloadFile(File file, String fileName, HttpServletRequest request, HttpServletResponse response) {
        try {
            if (request.getHeader("user-agent").contains("MSIE")) {
                fileName = new String(fileName.getBytes("gbk"), "iso-8859-1");
            } else {
                fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
            }
        } catch (Exception e) {
            log.error("", e);
        }
        try (InputStream is = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {
            response.reset();
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            byte[] b = new byte[102400];
            int len = 0;
            while ((len = is.read(b)) != -1) {
                os.write(b, 0, len);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}
